Why the heck would anyone want to write shell scripts? After all, a script is just a bunch of commands that could be issued from the shell prompt anyway. Well, sure. But how many times does a user have to reenter the same command before his fingers become knotted and arthritic? Wouldn't it be easier to store those commands in a file that can be executed by the computer? Certainly the CPU can execute commands faster than a human can type. Besides, isn't it smarter to modularize tasks so they can be combined and reused in an efficient manner? The fact of the matter is that shell scripts can provide this and more.
Shell scripts can help organize repeated commands. An administrator who often repeats a series of operations can record the command set into a file. The administrator can then instruct the shell to read the file and execute the commands within it. Thus, the task has been automated; one command now does the work of tens, hundreds, even thousands of others.
Shell scripting helps users handle the operating system smarter. The techniques presented in this discussion can be applied at the command line prompt. They are, after all, commands themselves; albeit, they are built into the Bourne shell. Still, by developing a habit of scripting at the command line, users can save time and effort by applying a script to a task rather than executing it by brute force. A good example is processing a set of files with the same command. A user can type the same command with a different file name over and over, or the user may write a loop that iterates over the files and automatically generates the same command set.
Furthermore, shell scripting is simpler than conventional programming. Unlike compiled languages such as C and C++, scripts are interpreted. There is no compiling statements into machine code and linking it with libraries to form an executable. The script is the executable. Commands entered into it are immediately ready to run. On the other hand, scripts run slower than compiled programs and are often harder to debug mainly because no debugging tools are available. Even so, shell scripts are easier to develop and modify.
Hopefully, the point is clear. Scripting is powerful, simple, and fast. Anything that someone can think of entering at the command line can be automated by a shell script:
At the risk of sounding cliche, a good shell script reads like a well written story. All right, maybe not a story, but it does take a form similar to a classical essay. It has an introduction, a body, and a conclusion. The introduction prepares the script much like the reader of an essay is prepared for the paper's contents by its introduction. A script's body presents commands to the shell just as an author presents an argument through an essay's body. And, in both cases, the conclusion wraps up loose ends. The shell script listed below illustrates this point. It is read from top to bottom with each line encountered being executed in sequence. Normally, the text is stored within a file that has execute permissions set. A user simply enters the file's name at the command line prompt, and the program is run. For illustrative purposes, the script is shown.
#!/bin/sh
# cleandir -- remove all the large, unused files from the current directory
echo "Removing all object files..."
rm *.o # all object files have a .o extension
echo "done."
echo "Removing core dumps..."
rm core # there's only ever one core
echo "done."
exit 0
Before analyzing the script, a quick note about comments is deserved. Comments are text that is ignored by the interpreter.
Programmers provide comments to describe their code to readers. Comments begin
with a hash mark (#). They may start anywhere within a line, and
the comment always extends from the hash mark to the end of the line. A comment
spans one line only; multiple line comments are not allowed. The script above
shows the two types of commenting. The file prologue comment extends across a whole line:
# cleandir -- remove all the large, unused files from the current directory
On the other hand, the comments following the remove statements are inlined. They share the same line with the command:
rm *.o # all object files have a .o extension ... rm core # there's only ever one core
The first line of the script is not a comment. Instead, it specifies which shell shall be used to process
the succeeding commands. Considering the comparisson of a script to an
essay, it can be said that the first line introduces the script's shell. It is
the introduction. The basic syntax for declaring the shell has the form:
#![full path to shell program]. The exclamation point following the
hash mark signals the interpreter that the line is not a comment. It tells the
interpreter that the text that follows is the path to a program in which to
execute the script. In the example above, the Bourne shell is used,
/bin/sh.
It is not mandatory for a script to specify what shell program to use. If no
such specification is given, then by default, the script is executed in a
subshell of the calling shell. In other words, the script uses the same shell as
the invoking shell. This is potentially dangerous. Suppose a user operates from
a different shell than the Bourne shell. The tcsh is a good
example. It has a much more user-friendly command line interface than
sh, and it uses the same syntax as the csh. This
syntax looks like C source code. It is very different from sh
syntax. Suppose also that the user writes a script with Bourne shell syntax but
neglects to make #!/bin/sh the first line. When the user invokes
the script from the tcsh prompt, the interpreter reads the script
and determines what shell to use. Since no shell is specified, it invokes a
subshell of the tcsh, and executes the script. Chances are the
program fails miserably. Section 9, Debugging
Hints contains an example that demonstrates such a failure.
A shell script's body is the list of commands following
the shell declaration. It tells the program's story: what is it doing and
how is it doing it. The shell processes the command body sequentially with
branching, looping, and function constructs being exceptions to the rule. Any
command that can be issued from a shell prompt may be included in a script's
body. The example's body is the set of echo and rm
statements:
echo "Removing all object files..." rm *.o # all object files have a .o extension echo "done." echo "Removing core dumps..." rm core # there's only ever one core echo "done."
This script tells the story of cleaning up a directory. It begins by
informing the invoker with the echo that it will remove the object
files. The next line actually performs this action with the rm on
all files containing a .o extenstion in their name. This command in
turn is followed by two more echo commands. The first indicates
that the previous action completed. The second informs the user what it will do
next. A second remove follows the two echo statements. It deletes
any local core dumps. After completing, control passes onto the last
echo which informs that user that the core dump removal
finished.
By default, a script concludes as soon as it has
reached the last statement in its body. At this point, it returns control to
its invoker. It also returns an exit status code. The exits code of a script is
the return status of the last command run by the script. It is possible,
however, to control this value by using the exit command. It takes the form exit
status where status can be any non-negative
integer. Zero usually indicates no errors were encountered by the script.
Non-zero values indicate detection of various faults defined by the script. It
is up to the programmer to determine what these faults are and to assign exit
codes to them. The example terminates gracefully hen the body has been executed.
It signals success by returning a zero status code:
... echo "done." exit 0
It must also be noted that unlike an essay, a
script can provide a conclusion anywhere within its body. To terminate the
execution of a script prior to reaching its end, the exit command
must be used. This is generally done when a program detects an unrecoverable
error such as insufficient parameterization. Once again, scripters are
responsible for determining errant conditions and deciding whether to terminate
the program or not.